package yang.yu.sunny.infra.redis;

import org.apache.commons.collections.map.HashedMap;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import yang.yu.sunny.domain.NamedEntity;
import yang.yu.sunny.domain.product.Product;
import yang.yu.sunny.domain.product.ProductCategory;
import yang.yu.sunny.domain.sales.Customer;
import yang.yu.sunny.infra.MasterDataService;
import yang.yu.sunny.infra.UpdatableMasterDataService;
import yang.yu.sunny.infra.persistence.product.ProductCategoryMapper;
import yang.yu.sunny.infra.persistence.product.ProductMapper;
import yang.yu.sunny.infra.persistence.sales.CustomerMapper;

import javax.inject.Inject;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;

/**
 * Created by yyang on 16/4/14.
 */
public class RedisMasterDataService implements UpdatableMasterDataService {

    private static final String PREFIX = "/MasterData";

    private static final String CUSTOMER_PREFIX = PREFIX + "/Customer/";

    private static final String PRODUCT_PREFIX = PREFIX + "/Product/";

    private static final String PRODUCT_CATEGORY_PREFIX = PREFIX + "/ProductCategory/";


    //private JedisCluster jedis;
    private Jedis jedis;

    private JedisCluster jedisCluster;

    @Inject
    private CustomerMapper customerMapper;

    @Inject
    private ProductCategoryMapper categoryMapper;

    @Inject
    private ProductMapper productMapper;

    private Map<Class, String> prefixMap = new HashedMap(){{
        put(Customer.class, CUSTOMER_PREFIX);
        put(Product.class, PRODUCT_PREFIX);
        put(ProductCategory.class, PRODUCT_CATEGORY_PREFIX);
    }};

    public RedisMasterDataService(String host, int port) {
        jedis = new Jedis(host, port);
    }

    public RedisMasterDataService(Map<String, Integer> hostAndPorts) {
        Set<HostAndPort> jedisClusterNodes = hostAndPorts.entrySet().stream()
                .map(entry -> new HostAndPort(entry.getKey(), entry.getValue()))
                .collect(Collectors.toSet());
        JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
    }

    public void setCustomerMapper(CustomerMapper customerMapper) {
        this.customerMapper = customerMapper;
    }

    public void setCategoryMapper(ProductCategoryMapper categoryMapper) {
        this.categoryMapper = categoryMapper;
    }

    public void setProductMapper(ProductMapper productMapper) {
        this.productMapper = productMapper;
    }

    @Override
    public List<String> nameOfCustomers(List<Long> ids) {
        return namesOf(Customer.class, ids);
    }

    @Override
    public List<String> nameOfCustomers(Long... ids) {
        return namesOf(Customer.class, ids);
    }

    @Override
    public List<String> nameOfProducts(List<Long> ids) {
        return namesOf(Product.class, ids);
    }

    @Override
    public List<String> nameOfProducts(Long... ids) {
        return namesOf(Product.class, ids);
    }

    @Override
    public List<String> nameOfProductCategories(List<Long> ids) {
        return namesOf(ProductCategory.class, ids);
    }

    @Override
    public List<String> nameOfProductCategories(Long... ids) {
        return namesOf(ProductCategory.class, ids);
    }

    private List<String> namesOf(Class<? extends NamedEntity> entityClass, List<Long> ids) {
        String[] idAsString = new String[ids.size()];
        int i = 0;
        for (Long id : ids) {
            idAsString[i] = id.toString();
            i++;
        }
        return jedis.hmget(prefixMap.get(entityClass), idAsString);
    }

    private List<String> namesOf(Class<? extends NamedEntity> entityClass, Long... ids) {
        String[] idAsString = new String[ids.length];
        int i = 0;
        for (Long id : ids) {
            idAsString[i] = id.toString();
            i++;
        }
        return jedis.hmget(prefixMap.get(entityClass), idAsString);
    }

    @Override
    public void update(NamedEntity... entities) {
        update(Arrays.asList(entities));
    }

    @Override
    public void update(Collection<? extends NamedEntity> entities) {
        if (entities.isEmpty()) {
            return;
        }
        Class entityClass = entities.iterator().next().getClass();
        String prefix = prefixMap.get(entityClass);
        Map<String, String> keyValues = entities.stream().collect(toMap(
                entity -> entity.getId().toString(), NamedEntity::getName
        ));
        jedis.hmset(prefixMap.get(entityClass), keyValues);
    }

    public void updateCache(Product product) {
        jedis.set(PRODUCT_PREFIX + product.getId(), product.getName());
    }

    public void updateCache(ProductCategory category) {
        jedis.set(PRODUCT_CATEGORY_PREFIX + category.getId(), category.getName());
    }

    private void writeCache(List<? extends NamedEntity> entities, Class<? extends NamedEntity> entityClass) {
        String prefix = prefixMap.get(entityClass);
        Map<String, String> keyValues = entities.stream().collect(toMap(
                entity -> entity.getId().toString(), NamedEntity::getName
        ));
        jedis.hmset(prefixMap.get(entityClass), keyValues);
    }

    @Override
    public void init() {
        initCustomers();
        initProducts();
        initProductCategories();
    }

    private void initCustomers() {
        writeCache(customerMapper.listAll(), Customer.class);
    }

    private void initProducts() {
        writeCache(productMapper.listAll(), Product.class);
    }

    private void initProductCategories() {
        writeCache(categoryMapper.listAll(), ProductCategory.class);
    }

}
